package com.example.yilanchatserver.business.freechat.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.ConvertException;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.HttpException;
import cn.hutool.http.HttpRequest;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.example.yilanchatserver.application.controller.vo.ErnieBotRespVO;
import com.example.yilanchatserver.business.freechat.*;
import com.example.yilanchatserver.business.freechat.service.FreeChatService;
import com.example.yilanchatserver.business.freechat.service.RedisService;
import com.example.yilanchatserver.business.freechat.service.dto.FreeChatDTO;
import com.example.yilanchatserver.business.freechat.service.dto.FreeChatPromptDTO;
import com.example.yilanchatserver.business.freechat.service.dto.ErnieBotMessageDTO;
import com.example.yilanchatserver.business.freechat.util.ErnieBotUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

import static com.example.yilanchatserver.business.freechat.enums.CommonConstants.ERNIEBOT_ACCESS_TOKEN_KEY;

/**
 * 备注：调用该API需到平台用户手册中--》》新手指南下载平台调用工具包
 */

@Slf4j
@Service
public class FreeChatServiceImpl implements FreeChatService {

    // 对话请求地址
    @Value("${wudao.url.modelRequestURL}")
    private String MODEL_REQUEST_URL;

    @Value("${wudao.url.modelPromptRequestURL}")
    private String MODEL_PROMPT_REQUEST_URL;

    // 获取token 请求地址
    @Value("${wudao.url.authTokenURL}")
    private String AUTH_TOKEN_URL;

    // 用户API KEY（请登录开放平台获取 https://maas.aminer.cn ）
    @Value("${wudao.key.apiKey}")
    private String API_KEY;

    // 用户PUBLIC KEY（请登录开放平台获取 https://maas.aminer.cn ）
    @Value("${wudao.key.publicKey}")
    private String PUBLIC_KEY;

    @Value("${chatgpt.url}")
    private String chatURL;

    @Value("${ernieBot.apiKey}")
    private String ernieBotApiKey;

    @Value("${ernieBot.secretKey}")
    private String ernieBotSecretKey;
    @Value("${ernieBot.modelURL}")
    private String ernieBotModelUrl;
    @Value("${ernieBot.accessTokenURL}")
    private String ernieBotAccessTokenURL;

    @Resource
    private RedisService redisService;


    private static final String tokenKey = "wudao:token";

    private String getToken() throws Exception {
        try {
            String token = redisService.get(tokenKey);
            if (StringUtils.isEmpty(token)) {
                Map<String, Object> resultMap = WuDaoUtils.getToken(AUTH_TOKEN_URL, API_KEY, PUBLIC_KEY);
                int code = (int) resultMap.get("code");
                if (code == 200) {
                    token = String.valueOf(resultMap.get("data"));
                    redisService.set(tokenKey, token, 8, TimeUnit.HOURS);
                } else {
                    throw new BusinessException(BusinessErrorCodes.DEFAULT_BUSINESS_ERROR);
                }
            }
            return token;
        } catch (Exception e) {
            log.error("获取token失败", e);
            throw e;
        }
//        return null;
    }

    private String getErnieBotAccessToken() throws Exception {
        try {
            String token = redisService.get(ERNIEBOT_ACCESS_TOKEN_KEY);
            if (StringUtils.isEmpty(token)) {
                token = ErnieBotUtils.getAccessToken(ernieBotAccessTokenURL, ernieBotApiKey, ernieBotSecretKey);
                redisService.set(ERNIEBOT_ACCESS_TOKEN_KEY, token, 8, TimeUnit.HOURS);
            }
            return token;
        } catch (Exception e) {
            log.error("获取token失败", e);
            throw e;
        }
    }

//    List<String> sensitiveList = ["不好意思"]

    @Override
    @Async("asyncServiceExecutor")
    public CompletableFuture getData(FreeChatDTO freeChatDTO) throws Exception {
        try {
            /**
             * 注意这里仅为了简化编码每一次请求都去获取token，
             * 线上环境token有过期时间， 客户端可自行缓存，
             * 过期后重新获取。
             */
            String authToken = getToken();

            if (ObjectUtil.isEmpty(authToken)) {
                System.out.println("获取鉴权token失败");
                throw new BusinessException(BusinessErrorCodes.DEFAULT_BUSINESS_ERROR);
            }

            Map<String, String> talkerParams = new HashMap<>();
            String preUuid = UUID.randomUUID().toString();

            Map<String, Object> paramsMap = new HashMap();
//            paramsMap.put("prompt", freeChatDTO.getPrompt());
            paramsMap.put("requestTaskNo", preUuid);
            paramsMap.put("xdid", freeChatDTO.getRobotId());
            List<String> tmp = new ArrayList<>();
            String proQ = "你是元宇宙展厅的一位数字人员工，现在对你要求如下：\n1.需要在科技展厅面对用户的问题给出严肃、简洁、准确的回答。\n2.如果你确实不知道，也请如实给出不知道的原因，并婉转地表达不知道的意思。\n请扮演上述角色。如果明白，请回答“明白”，无需回答其他信息。";
//            String proQ = "给你的要求如下：你的所有回答不得超过50字。如果明白，请回答“明白”";
            String proA = "明白。";

            Integer totalNum = 0;

            if (freeChatDTO.getTalkList() != null && freeChatDTO.getTalkList().size() != 0) {
                paramsMap.put("prompt", freeChatDTO.getTalkList().get(freeChatDTO.getTalkList().size() - 1).get("text") + "回答不超过200字");
                for (int i = freeChatDTO.getTalkList().size() - 2; i >= 0; i = i - 2) {
                    if (i % 2 != 1) { //必须为偶数
                        throw new BusinessException(BusinessErrorCodes.QAPAIRS_ERROR);
                    }
                    if (freeChatDTO.getTalkList().get(i - 1).get("text") == null || freeChatDTO.getTalkList().get(i).get("text") == null) {
                        continue;
                    }
                    Integer num = freeChatDTO.getTalkList().get(i - 1).get("text").length() + freeChatDTO.getTalkList().get(i).get("text").length();
                    if (totalNum + num > 500) { //控制token，小于等于500（不含初始QA对）
                        break;
                    }
                    totalNum += num;
                    tmp.add(freeChatDTO.getTalkList().get(i).get("text"));
                    tmp.add(freeChatDTO.getTalkList().get(i - 1).get("text") + "回答不超过200字");

                }
            } else {
                throw new BusinessException(BusinessErrorCodes.QAPAIRS_ERROR);
            }
            tmp.add(proA);
            tmp.add(proQ);
            Collections.reverse(tmp);

            paramsMap.put("history", tmp);
//            long startTime = System.currentTimeMillis();
            Map resultMap = WuDaoUtils.executeEngine(MODEL_REQUEST_URL, authToken, paramsMap);
//            long endTime = System.currentTimeMillis();
//            System.out.println(endTime-startTime);
            int code = (int) resultMap.get("code");
            if (code == 200) {
                Map data = (Map) resultMap.get("data");
                JSONArray choices = (JSONArray) data.get("choices");
                JSONObject result = choices.getJSONObject(0);
                String output = result.getString("content");
                output = output.substring(1, output.length() - 1);
                return CompletableFuture.completedFuture(BaseResponseEntity.SUCCESS(output));
            } else if (code == 1505 || code == 500) {
//                throw new BusinessException(BusinessErrorCodes.SENSITIVE_WORDS_EXIST);
                return CompletableFuture.completedFuture(BaseResponseEntity.ERROR(resultMap.get("msg")));
            } else {
                log.error(code + ":" + resultMap.get("msg"));
                return CompletableFuture.completedFuture(BaseResponseEntity.ERROR("该问题无法回答"));
            }
//            throw new BusinessException(BusinessErrorCodes.DEFAULT_BUSINESS_ERROR);

        } catch (Exception e) {
            log.error("获取数据失败", e);
            throw e;
        }
    }


    @Override
    @Async("asyncServiceExecutor")
    public CompletableFuture getDataWithPrompt_v1(FreeChatPromptDTO freeChatDTO) throws Exception {
        try {
            /**
             * 注意这里仅为了简化编码每一次请求都去获取token，
             * 线上环境token有过期时间， 客户端可自行缓存，
             * 过期后重新获取。
             */
            String authToken = getToken();

            if (ObjectUtil.isEmpty(authToken)) {
                System.out.println("获取鉴权token失败");
                throw new BusinessException(BusinessErrorCodes.DEFAULT_BUSINESS_ERROR);
            }

            Map<String, String> talkerParams = new HashMap<>();
            String preUuid = UUID.randomUUID().toString();

            Map<String, Object> paramsMap = new HashMap();
//            paramsMap.put("prompt", freeChatDTO.getPrompt());
            paramsMap.put("requestTaskNo", preUuid);
            paramsMap.put("xdid", freeChatDTO.getRobotId());
            List<String> tmp = new ArrayList<>();
//            String proQ = "你是元宇宙展厅的一位数字人员工，现在对你要求如下：\n1.需要在科技展厅面对用户的问题给出严肃、简洁、准确的回答。\n2.回答不超过200个汉字。\n3.如果你确实不知道，也请如实给出不知道的原因，并婉转地表达不知道的意思。\n请扮演上述角色。如果明白，请回答“明白”，无需回答其他信息。";
////            String proQ = "给你的要求如下：你的所有回答不得超过50字。如果明白，请回答“明白”";
//            String proA = "明白。";

            Integer totalNum = 0;

            if (freeChatDTO.getTalkList() != null && freeChatDTO.getTalkList().size() != 0) {
                paramsMap.put("prompt", freeChatDTO.getTalkList().get(freeChatDTO.getTalkList().size() - 1).get("text"));
                for (int i = freeChatDTO.getTalkList().size() - 2; i >= 0; i = i - 2) {
                    if (i % 2 != 1) { //必须为偶数
                        throw new BusinessException(BusinessErrorCodes.QAPAIRS_ERROR);
                    }
                    if (freeChatDTO.getTalkList().get(i - 1).get("text") == null || freeChatDTO.getTalkList().get(i).get("text") == null) {
                        continue;
                    }
                    Integer num = freeChatDTO.getTalkList().get(i - 1).get("text").length() + freeChatDTO.getTalkList().get(i).get("text").length();
                    if (totalNum + num > 500) { //控制token，小于等于500（不含初始QA对）
                        break;
                    }
                    totalNum += num;
                    tmp.add(freeChatDTO.getTalkList().get(i).get("text"));
                    tmp.add(freeChatDTO.getTalkList().get(i - 1).get("text"));

                }
            } else {
                throw new BusinessException(BusinessErrorCodes.QAPAIRS_ERROR);
            }
            if (freeChatDTO.getPrompt() != null) {
                for (int i = freeChatDTO.getPrompt().size() - 1; i >= 0; i = i - 2) {

                    if (i % 2 != 1) { //必须为偶数
                        throw new BusinessException(BusinessErrorCodes.QAPAIRS_ERROR);
                    }
                    if (freeChatDTO.getPrompt().get(i - 1).get("text") == null || freeChatDTO.getPrompt().get(i).get("text") == null) {
                        continue;
                    }
                    Integer num = freeChatDTO.getPrompt().get(i - 1).get("text").length() + freeChatDTO.getPrompt().get(i).get("text").length();
                    if (totalNum + num > 500) { //控制token，小于等于500（不含初始QA对）
                        break;
                    }
                    totalNum += num;
                    tmp.add(freeChatDTO.getPrompt().get(i).get("text"));
                    tmp.add(freeChatDTO.getPrompt().get(i - 1).get("text"));
                }
            }
            Collections.reverse(tmp);

            paramsMap.put("history", tmp);
//            long startTime = System.currentTimeMillis();
            Map resultMap = WuDaoUtils.executeEngine(MODEL_PROMPT_REQUEST_URL, authToken, paramsMap);
//            long endTime = System.currentTimeMillis();
//            System.out.println(endTime-startTime);
            int code = (int) resultMap.get("code");
            if (code == 200) {
                Map data = (Map) resultMap.get("data");
                String output = (String) data.get("outputText");
                return CompletableFuture.completedFuture(BaseResponseEntity.SUCCESS(output));
            } else if (code == 1505 || code == 500) {
//                throw new BusinessException(BusinessErrorCodes.SENSITIVE_WORDS_EXIST);
                return CompletableFuture.completedFuture(BaseResponseEntity.ERROR(resultMap.get("msg")));
            } else {
                log.error(code + ":" + resultMap.get("msg"));
                return CompletableFuture.completedFuture(BaseResponseEntity.ERROR("该问题无法回答"));
            }
//            throw new BusinessException(BusinessErrorCodes.DEFAULT_BUSINESS_ERROR);

        } catch (Exception e) {
            log.error("获取数据失败", e);
            throw e;
        }
    }

    @Override
    @Async("asyncServiceExecutor")
    public CompletableFuture getDataWithPrompt(FreeChatPromptDTO freeChatDTO) throws Exception {
        try {
            /**
             * 注意这里仅为了简化编码每一次请求都去获取token，
             * 线上环境token有过期时间， 客户端可自行缓存，
             * 过期后重新获取。
             */
            String authToken = getToken();

            if (ObjectUtil.isEmpty(authToken)) {
                System.out.println("获取鉴权token失败");
                throw new BusinessException(BusinessErrorCodes.DEFAULT_BUSINESS_ERROR);
            }

            Map<String, String> talkerParams = new HashMap<>();
            String preUuid = UUID.randomUUID().toString();

            System.out.println(preUuid);
            Map<String, Object> paramsMap = new HashMap();
            paramsMap.put("requestTaskNo", preUuid);

            List<Map<String, String>> qaPairs = new ArrayList<>();
            if (freeChatDTO.getPrompt() != null) {
                for (int i = 0; i < freeChatDTO.getPrompt().size(); i++) {

                    Map<String, String> tmp = freeChatDTO.getPrompt().get(i);
                    tmp.put("role", tmp.remove("talker"));
                    tmp.put("content", tmp.remove("text"));
                    qaPairs.add(tmp);
                }
            }


            for (int i = 0; i < freeChatDTO.getTalkList().size(); i++) {

                Map<String, String> tmp = freeChatDTO.getTalkList().get(i);
                if (Objects.equals(tmp.get("talker"), "bot")) {
                    tmp.put("talker", "assistant");
                }
                tmp.put("role", tmp.remove("talker"));
                tmp.put("content", tmp.remove("text"));
                qaPairs.add(tmp);
            }

            paramsMap.put("prompt", qaPairs);
//            long startTime = System.currentTimeMillis();
            Map resultMap = WuDaoUtils.executeEngine(MODEL_PROMPT_REQUEST_URL, authToken, paramsMap);
//            long endTime = System.currentTimeMillis();
//            System.out.println(endTime-startTime);
            int code = (int) resultMap.get("code");
            if (code == 200) {
                Map data = (Map) resultMap.get("data");
                String taskId = (String) data.get("task_id");
                Object fullBody = null;
                String output = null;
                String status = null;
                try {
                    while (!Objects.equals(status, "SUCCESS")) {
                        String body = HttpRequest.get("https://open.bigmodel.cn/api/paas/v3/model-api/-/async-invoke/" + taskId)
                                .header("Authorization", authToken)
                                .header("Content-Type", "application/json", false)
                                .execute()
                                .body();

                        JSONObject jsonObject = JSONObject.parseObject(body);
                        JSONObject bodyData = jsonObject.getJSONObject("data");
                        status = bodyData.getString("task_status");
                        TimeUnit.MILLISECONDS.sleep(100);
                    }
                    String body = HttpRequest.get("https://open.bigmodel.cn/api/paas/v3/model-api/-/async-invoke/" + taskId)
                            .header("Authorization", authToken)
                            .header("Content-Type", "application/json", false)
                            .execute()
                            .body();

                    JSONObject jsonObject = JSONObject.parseObject(body);
                    JSONObject bodyData = jsonObject.getJSONObject("data");
                    JSONArray choices = bodyData.getJSONArray("choices");
                    JSONObject result = choices.getJSONObject(0);
                    output = result.getString("content");
                    output = output.substring(1, output.length() - 1);

                } catch (HttpException e) {
                    throw new BusinessException(BusinessErrorCodes.DEFAULT_BUSINESS_ERROR);
                } catch (ConvertException e) {
                    throw new BusinessException(BusinessErrorCodes.GET_PARAM_ERROR);
                }
                return CompletableFuture.completedFuture(BaseResponseEntity.SUCCESS(output));
            } else if (code == 1505 || code == 500) {
//                throw new BusinessException(BusinessErrorCodes.SENSITIVE_WORDS_EXIST);
                return CompletableFuture.completedFuture(BaseResponseEntity.ERROR(resultMap.get("msg")));
            } else {
                log.error(code + ":" + resultMap.get("msg"));
                return CompletableFuture.completedFuture(BaseResponseEntity.ERROR("该问题无法回答"));
            }
//            throw new BusinessException(BusinessErrorCodes.DEFAULT_BUSINESS_ERROR);

        } catch (Exception e) {
            log.error("获取数据失败", e);
            throw e;
        }
    }

    @Override
    @Async("asyncServiceExecutor")
    public CompletableFuture getChatGPT(FreeChatDTO freeChatDTO) {
        Map<String, Object> paramMap = new HashMap<>();
        if (Objects.equals(freeChatDTO.getRobotId(), "#xd小呆6836")) {
            freeChatDTO.setRobotId("gpt-3.5-turbo");
        }
        paramMap.put("model", freeChatDTO.getRobotId());
//        List<Map<String, String>> dataList = new ArrayList<>();
//        dataList.add(new HashMap<String, String>(){{
//            put("role", "user");
//            put("content", txt);
//        }});
        List<Map<String, String>> qaPairs = new ArrayList<>();

        for (int i = 0; i < freeChatDTO.getTalkList().size(); i++) {

            Map<String, String> tmp = freeChatDTO.getTalkList().get(i);
            tmp.put("role", tmp.remove("talker"));
            tmp.put("content", tmp.remove("text"));
            qaPairs.add(tmp);
        }
//        Map<String, String> tmp = new HashMap<>();
//        tmp.put("role", "user");
//        tmp.put("content", freeChatDTO.getPrompt());
//        qaPairs.add(tmp);
        paramMap.put("qaPairs", qaPairs);
        JSONObject message = null;
        Object fullBody = null;
        try {
            String body = HttpRequest.post(chatURL)
                    .header("Content-Type", "application/json")
                    .body(JSON.toJSONString(paramMap))
                    .execute()
                    .body();

            JSONObject jsonObject = JSONObject.parseObject(body);
            fullBody = jsonObject;
            JSONArray choices = jsonObject.getJSONArray("choices");
            JSONObject result = choices.getJSONObject(0);
            message = result.getJSONObject("message");
        } catch (HttpException e) {
            throw new BusinessException(BusinessErrorCodes.DEFAULT_BUSINESS_ERROR);
        } catch (ConvertException e) {
            throw new BusinessException(BusinessErrorCodes.GET_PARAM_ERROR);
        }
        return CompletableFuture.completedFuture(BaseResponseEntity.SUCCESS(message.getString("content")));
//        return fullBody;

    }

    @Override
    @Async("asyncServiceExecutor")
    public CompletableFuture getErnieBotData(FreeChatDTO freeChatDTO) throws Exception {
        // 1.获取access_token
        String accessToken = this.getErnieBotAccessToken();
        // 2.组装请求参数
        List<ErnieBotMessageDTO> messages = new ArrayList<>();
        List<Map<String, String>> talkList = freeChatDTO.getTalkList();
        if (CollectionUtil.isNotEmpty(talkList)) {
            for (Map<String, String> item : talkList) {
                messages.add(new ErnieBotMessageDTO(item.get("talker"), item.get("text")));
            }
        }
        JSONObject bodyParams = new JSONObject();
        bodyParams.put("messages", messages);
        // 3.请求
        String message = "";
        try {
            String body = HttpRequest.post(ernieBotModelUrl + "?access_token=" + accessToken)
                    .header("Content-Type", "application/json")
                    .body(JSON.toJSONString(bodyParams))
                    .execute()
                    .body();

            ErnieBotRespVO resultJson = JSONObject.parseObject(body, ErnieBotRespVO.class);
            message = resultJson.getResult();
        } catch (HttpException e) {
            throw new BusinessException(BusinessErrorCodes.DEFAULT_BUSINESS_ERROR);
        } catch (ConvertException e) {
            throw new BusinessException(BusinessErrorCodes.GET_PARAM_ERROR);
        }
        return CompletableFuture.completedFuture(BaseResponseEntity.SUCCESS(message));
    }
}



